#include <mpi.h>

#include "GRASP.h"
#include "Kruskal.h"
#include "FileSTS.h"

#define NUM_ITER 50

#define TAG_CONS_SOL 98
#define TAG_CONS_AVAL 99
#define TAG_VNS_SOL 100
#define TAG_VNS_AVAL 101

#define RECV_CONS_SOL 102
#define RECV_CONS_AVAL 103

double startwtime = 0.0, endwtime;
int melhorIter = 0;

struct SolucaoStruct{
	double aval;
	char data[1000];
};

MPI_Datatype SolucaoType;
MPI_Datatype type[2] = {MPI_DOUBLE, MPI_CHAR};

int main(int argc, char** argv){
	if(argc == 3){
		int size, rank;
	
		MPI_Init(&argc, &argv);
		MPI_Comm_size(MPI_COMM_WORLD, &size);
		MPI_Comm_rank(MPI_COMM_WORLD, &rank);
	
		srand(time(NULL));
		
		//Inicializando os dados de arquivo
		Solucao::loadEntrada(argv[1]);
		double alpha = atof(argv[2]);
		GRASP g(Solucao::tamanho, alpha);
		
		int blocken[2] = {1, 1000};
		MPI_Aint disp[2] = {0,sizeof(double)};

		//Definindo o datatype.
		MPI_Type_struct(2, blocken, disp, type, &SolucaoType);
		MPI_Type_commit(&SolucaoType);

		//Pegando o tempo inicial
		if(rank == 0)
			startwtime = MPI_Wtime();

		SolucaoGRASPSTS *sbest = NULL;
		char *data = NULL;
		double aval;
		if(rank != 0){
			MPI_Status status;
			for(int i=0; i<NUM_ITER; i++){
				//Fase de Construcao e Busca Local.
				SolucaoGRASPSTS *sol = g.construcao();
				SolucaoGRASPSTS *sbl = g.buscaLocal(sol);
				delete sol;
				
				//data = sbl->print();
				SolucaoStruct ss;
				strcpy(ss.data, sbl->print());
				ss.aval = sbl->avaliacao;
				//Enviando a sua melhor solucao para o servidor.
				MPI_Send(&ss, 1, SolucaoType, 0, TAG_CONS_SOL, MPI_COMM_WORLD);
				
				SolucaoStruct sr;
				//Recebendo a melhor solucao do servidor. 
				MPI_Recv(&sr, 1, SolucaoType, 0, RECV_CONS_SOL, MPI_COMM_WORLD, &status);
				
				delete sbl;
				sbl = new SolucaoGRASPSTS(sr.data);
				sbl->avaliacao = sr.aval;
				
				//Executando o VNS.
				SolucaoGRASPSTS *svns = g.VNS(sbl);
				
				SolucaoStruct ss2;
				strcpy(ss2.data, svns->print());
				ss2.aval = svns->avaliacao;
				//Enviando a sua melhor solucao para o servidor.
				MPI_Send(&ss2, 1, SolucaoType, 0, TAG_VNS_SOL, MPI_COMM_WORLD);
				delete svns;
			}
		}else{
			for(int i=0; i<(NUM_ITER * 2 *(size-1)); i++){
				MPI_Status status;
				SolucaoStruct ss;
				int source;
				MPI_Recv(&ss, 1, SolucaoType, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, 
					&status);
				
				switch(status.MPI_TAG){
					case TAG_CONS_SOL:
						source = status.MPI_SOURCE;
						if(sbest == NULL){
							sbest = new SolucaoGRASPSTS(ss.data);
							sbest->avaliacao = ss.aval;
							melhorIter = i;
						}else{
							if(ss.aval < sbest->avaliacao){
								sbest = new SolucaoGRASPSTS(ss.data);
								sbest->avaliacao = ss.aval;
								melhorIter = i;
								//printf("CONS %s\n", ss.data);
								//printf("CONS %f\n", ss.aval);
							}
						}
						SolucaoStruct sr;
						strcpy(sr.data, sbest->print());
						sr.aval = sbest->avaliacao;
						MPI_Send(&sr, 1, SolucaoType, source, RECV_CONS_SOL, 
							MPI_COMM_WORLD);
						break;
					case TAG_VNS_SOL:
						if(ss.aval < sbest->avaliacao){
							sbest = new SolucaoGRASPSTS(ss.data);
							sbest->avaliacao = ss.aval;
							melhorIter = i;
							//printf("VNS %s\n", ss.data);
							//printf("VNS %f\n", ss.aval);
						}
						break;
					default:
						puts("Erro no sincronismo das mensagens.");
						exit(0);
						break; 
				}
			}
		}			
			
		if(rank == 0){
			endwtime = MPI_Wtime();
			FileSTS::printInstancia(alpha, sbest->tamanho, sbest->numClient, melhorIter, 
				sbest->avaliacao, (int)((endwtime - startwtime) * 1000));
			printf("Final: %s F: %f Tempo: %f\n", sbest->print(), sbest->avaliacao, 
				endwtime - startwtime);
		}
		
	}else{
		puts("<usage: grasp nameFile alpha>");
	}
	MPI_Finalize();
	return 0;
}

